
'''
Created on 2013-11-02

@author: Nich
'''



#from PyMud.objects.components import components
from PyMud.objects.baseobject import Entity

#from PyMud.room.room_components import room_components

#all_components = {}
#all_components.update(components)
#all_components.update(room_components)





class DBComponentSource():
    def __init__(self, component_list):
        
        self.component_list = component_list
        
            
    def get_component(self, oid, component_name, session):
        Table = self.component_list[component_name]
        return session.query(Table).filter(Table.entity_id==oid).one()
       
    
    def get_supported_subset(self, component_list):
        return list(filter(self.has, component_list))
    
    def has(self, component_name):
        return component_name in self.component_list
    
    def get_entities_for_component(self, component_name, session):
        Table = self.component_list[component_name]
        return [component.entity_id for component in session.query(Table)]
        
    
    def create_component_data(self, component_name, component, entity_id, data, session):
        c = None
        if data is None:
            c = component(entity_id)
        else:
            c = component(entity_id, **data)
        session.add(c)
        
    
    def has_entity(self, component_name, entity_id, session):
        from sqlalchemy.sql import exists
        Table = self.component_list[component_name]
        entity_exists = session.query(exists().where(Table.entity_id==entity_id)).scalar()
        return entity_exists
            
    def add_component_to_object(self, component_name, entity_id, data, session):
        component = self.component_list[component_name]
        
        if not self.has_entity(component_name, entity_id, session):
            self.create_component_data(component_name, component, entity_id, data, session)
                #object.components.add(component_name)
        else:
            raise AttributeError("Can't add a component more than once to the same object")
    
    def get_entities_with_components(self, component_list, session=None):
        q = session.query(Entity)
        for comp in component_list:
            Table = self.component_list[comp]
            q = q.filter(Entity.id.in_(session.query(Table.entity_id)))
        
        entities = q.all()
        return [e.id for e in entities]


    def get_component_for_entities(self, entity_ids, component_name, session):
        Table = self.component_list[component_name]
        comps = session.query(Table).filter(Table.entity_id.in_(entity_ids))
        return comps
        
    def create_entity(self, session):
        e = Entity()
        session.add(e)
        return e
    
    
        
        

class ArrayComponentSource():
    def __init__(self, component_list):
        self.component_list = component_list
        self.component_object = {}
        
        for comp_name in component_list.keys():
            self.component_object[comp_name] = {}
        
        
    def get_component(self, oid, component, session=None):
        if oid in self.component_object[component.__compname__]:
            return self.component_object[component.__compname__][oid]   
        else:
            raise AttributeError("{0} has no component named {1}".format(oid, component.__compname__))
    
    def get_supported_subset(self, component_list):
        return list(filter(self.has, component_list))
        
    def has(self, component_name):
        return component_name in self.component_list
    
    def get_entities_for_component(self, component_name, session=None):
        return list(self.component_object[component_name].keys())
    
    def create_component_data(self, component_name, entity_id, data, session=None):
        component = self.component_list[component_name]
        if data is None:
            self.component_object[component_name][entity_id] = component(entity_id)
        else:
            self.component_object[component_name][entity_id] = component(entity_id, **data)
            

    def has_entity(self, component_name, entity_id, session=None):
        return entity_id in self.component_object[component_name]

    def add_component_to_object(self, component_name, entity_id, data, session=None):
        if not self.has_entity(component_name, entity_id, session):
            self.create_component_data(component_name, entity_id, data, session=None)
                #object.components.add(component_name)
        else:
            raise AttributeError("Can't add a component more than once to the same object")
        
    
    def get_entities_with_components(self, component_list, session=None):
        entities = []
        for component_name in component_list:
            entities.append(self.get_entities_for_component(component_name, session))
        
        if len(entities) == 0:
            return []
        
        e_list = set(entities[0])
        for e in entities[1:]:
            e_list = e_list.intersection(set(e))
        
        
        return e_list
    
    def get_component_for_entities(self, entity_ids, component_name, session=None):
        components = []
        for e_id in entity_ids:
            if self.has_entity(component_name, e_id):
            
                components.append(self.component_object[component_name][e_id])
                
            else:
                raise AttributeError("No entities with id "+e_id+" found for component "+component_name)
        return components
            
    
    def create_entity(self, session = None):
        return Entity()
    
    
    
    

class ComponentManager(object):
    
    def __init__(self, component_sources = None):
        self.component_sources = component_sources
        
    
    def get_component(self, entity_id, component_name, session):
        for component_source in self.component_sources:
            if component_source.has(component_name):
                return component_source.get_component(entity_id, component_name, session)
            
    def has_component(self, component_name):
        return all([cs.has(component_name) for cs in self.component_sources])
            
            
                
                          

    def add_component_to_object(self, component_name, entity_id, data = None, session = None):
        for component_source in self.component_sources: 
            if component_source.has(component_name):
                component_source.add_component_to_object(component_name, entity_id, data, session)
                break
        else:
            raise AttributeError("Component not found")
        
        
    def create_entity(self, entity_spec, session=None):
        
        entity = self.component_sources[0].create_entity(session)
        
        for component_name, component_data in entity_spec.items():
            self.add_component_to_object(component_name, entity.id, component_data, session)
            
        return entity
    
    
    def get_entities_for_component(self, component_name, session):
        entities = []
        for component_source in self.component_sources: 
            if component_source.has(component_name):
                entities += component_source.get_entities_for_component(component_name, session)
        return entities
    
    def get_entities_with_components(self, component_list, session=None):
        entities_from_sources = []
        components_covered = []
        
        for component_source in self.component_sources:
            supported_components = component_source.get_supported_subset(component_list)
            entities_from_sources.append(component_source.get_entities_with_components(supported_components, session))
            components_covered += supported_components
            
        if sorted(components_covered) != sorted(component_list):
            raise AttributeError("One or more components not found in one of the lists. Got: "+str(components_covered)+" Expected: "+str(component_list))
        
        entities = entities_from_sources[0]
        for entity_list in entities_from_sources[1:]:
            entities = list(set(entities).intersection(set(entity_list)))
        return entities
    
    def get_components_for_entities(self, entity_ids, component_name, session):
        components = []
        for component_source in self.component_sources: 
                if component_source.has(component_name):
                    components += component_source.get_component_for_entities(entity_ids, component_name, session)
        
        return components
        
        
        
        
    
    
            
    
    

        
    
    
    
    
    
    
    
    
    
    
    
        
        